package com.fancy.recommendation.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fancy.recommendation.dto.request.RecommendationRequestDTO;
import com.fancy.recommendation.dto.response.RecommendationResponseVO;
import com.fancy.recommendation.entity.Recommendation;
import com.fancy.recommendation.entity.UserBehavior;
import com.fancy.recommendation.entity.UserPreference;
import com.fancy.recommendation.mapper.RecommendationMapper;
import com.fancy.recommendation.mapper.UserBehaviorMapper;
import com.fancy.recommendation.mapper.UserPreferenceMapper;
import com.fancy.recommendation.service.RecommendationService;
import com.fancy.recommendation.service.UserBehaviorService;
import com.fancy.recommendation.constant.RecommendationConstants;
import com.fancy.imasclient.client.MusicServiceClient;
import com.fancy.common.dto.response.Result;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 推荐算法服务实现类
 *
 * @author fancy
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class RecommendationServiceImpl implements RecommendationService {

    private final RecommendationMapper recommendationMapper;
    private final UserBehaviorMapper userBehaviorMapper;
    private final UserPreferenceMapper userPreferenceMapper;
    private final UserBehaviorService userBehaviorService;
    private final StringRedisTemplate stringRedisTemplate;
    private final ObjectMapper objectMapper;
    private final MusicServiceClient musicServiceClient;

    @Value("${recommendation.algorithm.cache-duration:60}")
    private Integer cacheDuration;

    @Override
    public RecommendationResponseVO.RecommendationListVO getPersonalRecommendations(RecommendationRequestDTO request) {
        String cacheKey = RecommendationConstants.CacheKey.RECOMMENDATION_PREFIX +
            "personal:" + request.getUserId() + ":" + request.getRecType();

        try {
            // 尝试从缓存获取
            if (!Boolean.TRUE.equals(request.getRefreshCache())) {
                String cached = stringRedisTemplate.opsForValue().get(cacheKey);
                if (cached != null) {
                    RecommendationResponseVO.RecommendationListVO cachedResult =
                        objectMapper.readValue(cached, RecommendationResponseVO.RecommendationListVO.class);
                    log.info("从缓存获取推荐结果: userId={}", request.getUserId());
                    return cachedResult;
                }
            }
        } catch (Exception e) {
            log.warn("获取缓存推荐失败", e);
        }

        // 生成新的推荐结果
        RecommendationResponseVO.RecommendationListVO result = generatePersonalRecommendations(request);

        // 缓存推荐结果
        try {
            stringRedisTemplate.opsForValue().set(cacheKey, objectMapper.writeValueAsString(result),
                cacheDuration, TimeUnit.MINUTES);
        } catch (Exception e) {
            log.warn("缓存推荐结果失败", e);
        }

        return result;
    }

    @Override
    public List<Recommendation> collaborativeFiltering(Long userId, String targetType, Integer size) {
        log.info("开始协同过滤推荐: userId={}, targetType={}, size={}", userId, targetType, size);

        // 1. 获取用户相似用户
        List<Map<String, Object>> similarUsers = userBehaviorService.getSimilarUsers(userId, 20);

        if (CollectionUtils.isEmpty(similarUsers)) {
            log.info("未找到相似用户，回退到热门推荐");
            return trendingRecommend(targetType, size);
        }

        // 2. 收集相似用户喜欢的内容
        List<Long> similarUserIds = similarUsers.stream()
            .map(user -> ((Number) user.get("user_id")).longValue())
            .collect(Collectors.toList());

        // 3. 获取用户已有行为的目标ID，避免重复推荐
        Set<Long> userTargetIds = getUserTargetIds(userId, targetType);

        // 4. 基于相似用户行为生成推荐
        Map<Long, Double> targetScores = new HashMap<>();

        for (Long similarUserId : similarUserIds) {
            List<UserBehavior> behaviors = userBehaviorMapper.getRecentBehaviors(similarUserId, 100);

            for (UserBehavior behavior : behaviors) {
                if (behavior.getTargetType().equals(targetType) &&
                    !userTargetIds.contains(behavior.getTargetId())) {

                    double score = behavior.getBehaviorValue().doubleValue();
                    targetScores.merge(behavior.getTargetId(), score, Double::sum);
                }
            }
        }

        // 5. 生成推荐结果
        return targetScores.entrySet().stream()
            .sorted(Map.Entry.<Long, Double>comparingByValue().reversed())
            .limit(size)
            .map(entry -> createRecommendation(userId, RecommendationConstants.AlgorithmType.COLLABORATIVE_FILTERING,
                entry.getKey(), targetType, BigDecimal.valueOf(entry.getValue()),
                "基于相似用户的协同过滤推荐"))
            .collect(Collectors.toList());
    }

    @Override
    public List<Recommendation> contentBasedRecommend(Long userId, String targetType, Integer size) {
        log.info("开始基于内容的推荐: userId={}, targetType={}, size={}", userId, targetType, size);

        // 1. 获取用户偏好
        List<UserPreference> preferences = userPreferenceMapper.getUserPreferencesByType(userId, targetType);

        if (CollectionUtils.isEmpty(preferences)) {
            log.info("用户无偏好数据，回退到热门推荐");
            return trendingRecommend(targetType, size);
        }

        // 2. 获取用户已有行为的目标ID
        Set<Long> userTargetIds = getUserTargetIds(userId, targetType);

        // 3. 基于偏好计算推荐分数
        Map<Long, Double> targetScores = new HashMap<>();

        for (UserPreference preference : preferences) {
            try {
                // 这里需要根据偏好值查找相关内容
                // 实际实现中需要调用音乐服务来获取匹配的内容
                Long targetId = Long.valueOf(preference.getPreferenceValue());

                if (!userTargetIds.contains(targetId)) {
                    double score = preference.getWeight().doubleValue();
                    targetScores.put(targetId, score);
                }
            } catch (NumberFormatException e) {
                log.warn("偏好值格式错误: {}", preference.getPreferenceValue());
            }
        }

        // 4. 生成推荐结果
        return targetScores.entrySet().stream()
            .sorted(Map.Entry.<Long, Double>comparingByValue().reversed())
            .limit(size)
            .map(entry -> createRecommendation(userId, RecommendationConstants.AlgorithmType.CONTENT_BASED,
                entry.getKey(), targetType, BigDecimal.valueOf(entry.getValue()),
                "基于内容偏好的推荐"))
            .collect(Collectors.toList());
    }

    @Override
    public List<Recommendation> trendingRecommend(String targetType, Integer size) {
        log.info("开始热门推荐: targetType={}, size={}", targetType, size);

        // 获取最近7天的热门内容
        List<Map<String, Object>> hotContent = userBehaviorService.getHotContent(
            RecommendationConstants.ActionType.PLAY, 7, size * 2);

        return hotContent.stream()
            .filter(content -> targetType.equals(content.get("target_type")))
            .limit(size)
            .map(content -> createRecommendation(null, RecommendationConstants.AlgorithmType.TRENDING,
                ((Number) content.get("target_id")).longValue(), targetType,
                BigDecimal.valueOf(((Number) content.get("total_score")).doubleValue()),
                "热门推荐"))
            .collect(Collectors.toList());
    }

    @Override
    public List<Recommendation> hybridRecommend(Long userId, String targetType, Integer size) {
        log.info("开始混合推荐: userId={}, targetType={}, size={}", userId, targetType, size);

        int collaborativeSize = size / 2;
        int contentBasedSize = size / 3;
        int trendingSize = size - collaborativeSize - contentBasedSize;

        List<Recommendation> result = new ArrayList<>();

        // 协同过滤推荐
        try {
            List<Recommendation> collaborative = collaborativeFiltering(userId, targetType, collaborativeSize);
            result.addAll(collaborative);
        } catch (Exception e) {
            log.warn("协同过滤推荐失败", e);
        }

        // 基于内容推荐
        try {
            List<Recommendation> contentBased = contentBasedRecommend(userId, targetType, contentBasedSize);
            result.addAll(contentBased);
        } catch (Exception e) {
            log.warn("基于内容推荐失败", e);
        }

        // 热门推荐补充
        try {
            List<Recommendation> trending = trendingRecommend(targetType, trendingSize);
            result.addAll(trending);
        } catch (Exception e) {
            log.warn("热门推荐失败", e);
        }

        // 去重并按分数排序
        return result.stream()
            .collect(Collectors.toMap(
                Recommendation::getTargetId,
                r -> r,
                (existing, replacement) -> existing.getScore().compareTo(replacement.getScore()) > 0 ? existing : replacement
            ))
            .values()
            .stream()
            .sorted((r1, r2) -> r2.getScore().compareTo(r1.getScore()))
            .limit(size)
            .collect(Collectors.toList());
    }

    @Override
    public List<Recommendation> getSimilarRecommendations(String targetType, Long targetId, Integer limit) {
        log.info("获取相似推荐: targetType={}, targetId={}, limit={}", targetType, targetId, limit);

        // 获取与指定目标有相似行为的用户
        List<UserBehavior> behaviors = userBehaviorMapper.selectList(
            new LambdaQueryWrapper<UserBehavior>()
                .eq(UserBehavior::getTargetType, targetType)
                .eq(UserBehavior::getTargetId, targetId)
                .orderByDesc(UserBehavior::getCreateTime)
                .last("LIMIT 100")
        );

        if (CollectionUtils.isEmpty(behaviors)) {
            return Collections.emptyList();
        }

        // 收集这些用户的其他行为
        Set<Long> userIds = behaviors.stream()
            .map(UserBehavior::getUserId)
            .collect(Collectors.toSet());

        Map<Long, Double> similarTargetScores = new HashMap<>();

        for (Long userId : userIds) {
            List<UserBehavior> userBehaviors = userBehaviorMapper.getRecentBehaviors(userId, 50);

            for (UserBehavior behavior : userBehaviors) {
                if (behavior.getTargetType().equals(targetType) &&
                    !behavior.getTargetId().equals(targetId)) {

                    double score = behavior.getBehaviorValue().doubleValue();
                    similarTargetScores.merge(behavior.getTargetId(), score, Double::sum);
                }
            }
        }

        return similarTargetScores.entrySet().stream()
            .sorted(Map.Entry.<Long, Double>comparingByValue().reversed())
            .limit(limit)
            .map(entry -> createRecommendation(null, RecommendationConstants.AlgorithmType.SIMILAR,
                entry.getKey(), targetType, BigDecimal.valueOf(entry.getValue()),
                "相似内容推荐"))
            .collect(Collectors.toList());
    }

    @Override
    @Transactional
    public void refreshUserRecommendations(Long userId) {
        log.info("刷新用户推荐: userId={}", userId);

        // 清除缓存
        Set<String> keys = stringRedisTemplate.keys(
            RecommendationConstants.CacheKey.RECOMMENDATION_PREFIX + "personal:" + userId + ":*");
        if (keys != null && !keys.isEmpty()) {
            stringRedisTemplate.delete(keys);
        }

        // 删除旧的推荐结果
        recommendationMapper.delete(
            new LambdaQueryWrapper<Recommendation>()
                .eq(Recommendation::getUserId, userId)
                .lt(Recommendation::getCreateTime, LocalDateTime.now().minusHours(1))
        );

        // 生成新的推荐结果
        generateRecommendations(userId, RecommendationConstants.RecType.SONG,
            RecommendationConstants.AlgorithmType.HYBRID);
        generateRecommendations(userId, RecommendationConstants.RecType.ALBUM,
            RecommendationConstants.AlgorithmType.HYBRID);
        generateRecommendations(userId, RecommendationConstants.RecType.PLAYLIST,
            RecommendationConstants.AlgorithmType.HYBRID);

        log.info("用户推荐刷新完成: userId={}", userId);
    }

    @Override
    @Transactional
    public void generateRecommendations(Long userId, String recType, String algorithm) {
        log.info("生成推荐结果: userId={}, recType={}, algorithm={}", userId, recType, algorithm);

        List<Recommendation> recommendations;

        switch (algorithm.toLowerCase()) {
            case "collaborative_filtering":
                recommendations = collaborativeFiltering(userId, recType, 20);
                break;
            case "content_based":
                recommendations = contentBasedRecommend(userId, recType, 20);
                break;
            case "trending":
                recommendations = trendingRecommend(recType, 20);
                break;
            case "hybrid":
            default:
                recommendations = hybridRecommend(userId, recType, 20);
                break;
        }

        // 设置推荐结果属性
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime expireTime = now.plusHours(cacheDuration / 60);

        for (Recommendation recommendation : recommendations) {
            recommendation.setUserId(userId);
            recommendation.setRecType(recType);
            recommendation.setAlgorithm(algorithm);
            recommendation.setStatus(RecommendationConstants.RecommendationStatus.VALID);
            recommendation.setCreateTime(now);
            recommendation.setExpireTime(expireTime);
        }

        // 批量插入
        if (!CollectionUtils.isEmpty(recommendations)) {
            recommendationMapper.batchInsert(recommendations);
            log.info("批量插入推荐结果: 数量={}", recommendations.size());
        }
    }

    @Override
    public List<Recommendation> getCachedRecommendations(Long userId, String recType, Integer limit) {
        return recommendationMapper.getValidRecommendations(userId, recType, limit);
    }

    @Override
    @Transactional
    public void updateRecommendationFeedback(Long recommendationId, String feedback) {
        log.info("更新推荐反馈: recommendationId={}, feedback={}", recommendationId, feedback);

        Recommendation recommendation = recommendationMapper.selectById(recommendationId);
        if (recommendation == null) {
            log.warn("推荐记录不存在: recommendationId={}", recommendationId);
            return;
        }

        // 根据反馈调整推荐状态或分数
        if ("dislike".equals(feedback)) {
            recommendationMapper.updateRecommendationStatus(recommendationId,
                RecommendationConstants.RecommendationStatus.INVALID);
        }

        // 可以在这里记录用户反馈，用于改进推荐算法
        log.info("推荐反馈处理完成: recommendationId={}", recommendationId);
    }

    /**
     * 生成个性化推荐
     */
    private RecommendationResponseVO.RecommendationListVO generatePersonalRecommendations(RecommendationRequestDTO request) {
        String algorithm = request.getAlgorithm() != null ? request.getAlgorithm() :
            RecommendationConstants.AlgorithmType.HYBRID;

        List<Recommendation> recommendations = hybridRecommend(request.getUserId(),
            request.getRecType(), request.getSize());

        List<RecommendationResponseVO> recommendationVOs = recommendations.stream()
            .map(this::convertToVO)
            .collect(Collectors.toList());

        // 增强推荐结果，添加目标详细信息
        enrichRecommendationDetails(recommendationVOs);

        RecommendationResponseVO.RecommendationListVO result = new RecommendationResponseVO.RecommendationListVO();
        result.setTotalCount(recommendationVOs.size());
        result.setAlgorithm(algorithm);
        result.setGenerateTime(LocalDateTime.now());
        result.setRecommendations(recommendationVOs);

        return result;
    }

    /**
     * 获取用户已有行为的目标ID
     */
    private Set<Long> getUserTargetIds(Long userId, String targetType) {
        List<UserBehavior> userBehaviors = userBehaviorMapper.selectList(
            new LambdaQueryWrapper<UserBehavior>()
                .eq(UserBehavior::getUserId, userId)
                .eq(UserBehavior::getTargetType, targetType)
                .ge(UserBehavior::getCreateTime, LocalDateTime.now().minusDays(30))
        );

        return userBehaviors.stream()
            .map(UserBehavior::getTargetId)
            .collect(Collectors.toSet());
    }

    /**
     * 创建推荐对象
     */
    private Recommendation createRecommendation(Long userId, String algorithm, Long targetId,
                                              String targetType, BigDecimal score, String reason) {
        Recommendation recommendation = new Recommendation();
        recommendation.setUserId(userId);
        recommendation.setTargetId(targetId);
        recommendation.setTargetType(targetType);
        recommendation.setScore(score);
        recommendation.setReason(reason);
        recommendation.setAlgorithm(algorithm);
        recommendation.setStatus(RecommendationConstants.RecommendationStatus.VALID);
        recommendation.setCreateTime(LocalDateTime.now());
        return recommendation;
    }

    /**
     * 转换为VO对象
     */
    private RecommendationResponseVO convertToVO(Recommendation recommendation) {
        RecommendationResponseVO vo = new RecommendationResponseVO();
        vo.setRecommendationId(recommendation.getId());
        vo.setRecType(recommendation.getRecType());
        vo.setTargetId(recommendation.getTargetId());
        vo.setTargetType(recommendation.getTargetType());
        vo.setScore(recommendation.getScore());
        vo.setReason(recommendation.getReason());
        vo.setAlgorithm(recommendation.getAlgorithm());
        vo.setCreateTime(recommendation.getCreateTime());
        vo.setExpireTime(recommendation.getExpireTime());
        return vo;
    }

    /**
     * 增强推荐结果，添加目标详细信息
     */
    private void enrichRecommendationDetails(List<RecommendationResponseVO> recommendationVOs) {
        for (RecommendationResponseVO vo : recommendationVOs) {
            try {
                if (RecommendationConstants.RecType.SONG.equals(vo.getTargetType())) {
                    // 调用音乐服务获取歌曲详细信息
                    Result<?> songInfo = musicServiceClient.getSongBasicInfo(vo.getTargetId());
                    if (songInfo != null && songInfo.getData() != null) {
                        vo.setTargetInfo(songInfo.getData());
                    }
                }
                // 可以添加更多类型的详细信息获取
            } catch (Exception e) {
                log.warn("获取推荐目标详细信息失败: targetId={}, targetType={}",
                    vo.getTargetId(), vo.getTargetType(), e);
            }
        }
    }
}
